/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ialsynth2012;


/**
 *
 * @author Jonas & Jesper
 */

import com.jsyn.JSyn;
import com.jsyn.Synthesizer;
import com.jsyn.data.DoubleTable;
import com.jsyn.instruments.WaveShapingVoice;
import com.jsyn.data.SegmentedEnvelope;
import com.jsyn.unitgen.Add;
import com.jsyn.unitgen.LineOut;
import com.jsyn.unitgen.UnitVoice;
import com.jsyn.unitgen.Multiply;
import com.jsyn.util.VoiceAllocator;
import com.jsyn.util.VoiceFactory;
import com.softsynth.jsyn.EqualTemperedTuning;
import com.softsynth.math.ChebyshevPolynomial;
import com.softsynth.math.PolynomialTableData;
import com.softsynth.shared.time.TimeStamp;

public class SynthInstrument {
     
  
    private float attackRate = 0.01f, attackPeak = 0.9f, decayRate = 0.3f, sustainLevel = 0.5f, releaseRate = 0.5f;
            // Create an amplitude envelope and fill it with data.
        double[] envData = {
                attackRate, attackPeak, // duration,value pair 0, "attack"
                decayRate, sustainLevel, // pair 1, "decay"
                releaseRate, 0.0  // pair 2, "release"
        };
        
        double[] modEnvData = {
                attackRate, 0.9, // duration,value pair 0, "attack"
                decayRate, 0.1, // pair 1, "decay"
                releaseRate, 0.0  // pair 2, "release"
        };
        
        SegmentedEnvelope ampEnvelope = new SegmentedEnvelope( envData );
        SegmentedEnvelope modEnvelope = new SegmentedEnvelope( modEnvData );

        
 	private VoiceAllocator allocator;
      	private final static int MAX_VOICES = 8;

	private Synthesizer synth;
	private Add mixer;
        private Multiply volR, volL;
	private LineOut lineOut;
        private DoubleTable table;
        private ialsynth2012.SynthInstrument.MyVoiceFactory factory;
       	private final static int CHEBYSHEV_ORDER = 11;

        
        
    PresetHandler presetHandler = new PresetHandler();
    public SynthInstrument(){
        
        // Hang at end of decay segment to provide a "sustain" segment.
        ampEnvelope.setSustainBegin( 2 );
        ampEnvelope.setSustainEnd( 2 );
        modEnvelope.setSustainBegin( 2 );
        modEnvelope.setSustainEnd( 2 );
        // a comment
        synth = JSyn.createSynthesizer();

        // Make table with Chebyshev polynomial to share among voices
        PolynomialTableData chebData = new PolynomialTableData(
        ChebyshevPolynomial.T( CHEBYSHEV_ORDER ), 1024 );
        table = new DoubleTable( chebData.getData() );
        allocator = new VoiceAllocator( MAX_VOICES );
        factory = new MyVoiceFactory();
        allocator.setVoiceFactory( factory );
        synth.add( mixer = new Add() );
        synth.add( lineOut = new LineOut() );
        synth.add( volL = new Multiply() );
        synth.add( volR = new Multiply() );
        
        mixer.output.connect( 0, volL.inputA, 0 );
        mixer.output.connect( 0, volR.inputA, 0 );
        volL.inputB.set(0, 1.0);
        volR.inputB.set(0, 1.0);

        volL.output.connect( 0, lineOut.input, 0 );
        volR.output.connect( 0, lineOut.input, 1 );
        
        // Start synthesizer using default stereo output at 44100 Hz.
        synth.start();
        lineOut.start();
    }
    
    	public void noteOff( int noteNumber )
	{
		UnitVoice voice = allocator.off( noteNumber );
		if( voice != null )
		{
			voice.noteOff( new TimeStamp( synth.getCurrentTime() ) );
		}
	}

	public void noteOn( int noteNumber )
	{
		UnitVoice voice = allocator.allocate( noteNumber );
		double frequency = indexToFrequency( noteNumber );
		double amplitude = 0.1;
		TimeStamp timeStamp = new TimeStamp( synth.getCurrentTime() );
		voice.noteOn( timeStamp, frequency, amplitude );
	}
        
        public void setAttackTime(float rate)
        {
            envData[0] = rate;
            modEnvData[0] = rate;
            refreshEnvelopes();
        }
        
        public void setDecayTime(float rate)
        {
            envData[2] = rate;
            modEnvData[2] = rate;
            refreshEnvelopes();
        }
        
        public void setSustainLevel(float level)
        {
            envData[3] = level;
            refreshEnvelopes();
        }
        
        public void setReleaseRate(float rate)
        {
            envData[4] = rate;
            modEnvData[4] = rate;
            refreshEnvelopes();
        }
        private void refreshEnvelopes()
        {
            ampEnvelope = new SegmentedEnvelope( envData );
            modEnvelope = new SegmentedEnvelope( modEnvData );
        }
        public void setVolume(float volume)
        {
            volL.inputB.set(0, (double)volume);
            volR.inputB.set(0, (double)volume);
        }
        
	double indexToFrequency( int pitch )
	{
		return EqualTemperedTuning.getMIDIFrequency( (int) (pitch + 36) );
	}
    
                        class MyVoiceFactory implements VoiceFactory
                        {
                            public UnitVoice createVoice( int tag )
                            {
                                    WaveShapingVoice voice = new WaveShapingVoice();
                                    voice.function.set( table );
                                    voice.setAmpEnvelope(ampEnvelope);
                                    voice.setRangeEnvelope(modEnvelope);
                                    synth.add( voice );
                                    // TODO Pass in time so we do not start pulling data too soon.
                                    voice.getOutput().connect( mixer.inputA );
                                    return voice;
                            }
                        }
}
